#pragma once

#include <cstdlib>
#include <cstring>
#include <functional>
#include <string>
#include <unistd.h>
#include <unordered_map>
#include <vector>

#include "netlib/base/logger.h"
#include "netlib/base/noncopyable.h"
#include "readline/history.h"
#include "readline/readline.h"

namespace netlib {

class REPL : NonCopyable {
public:
	using CommandFunction = std::function<int(char*)>;

	explicit REPL(const char* sym = "nnet> ") : symbol_(sym) {
		for (auto cmd : kQuitCmds) {
			AddCommand(std::move(cmd), [](char* in) { return Quit(in); });
		}
		using_history();
	}
	~REPL() {
		free(input_);
		rl_clear_history();
	}

	void Run() {
		while ((input_ = readline(symbol_)) != nullptr) {
			auto [cmd, args] = ExtractCommand(input_);
			auto it = cmd_map_.find(cmd);
			if (it == cmd_map_.end()) {
				LOG_ERROR << "Can't find command: " << cmd;
				continue;
			}
			auto res = it->second(args);
			if (res != 0) {
				break;
			}
		}
	}
	void AddCommand(std::string&& cmd, CommandFunction&& func) { cmd_map_.emplace(cmd, func); }

private:
	auto ExtractCommand(char* line) -> std::pair<std::string, char*> {
		auto pos = std::strchr(line, ' ');
		if (pos == nullptr) {
			return {std::string(line), nullptr};
		}
		return {std::string(line, pos - line), pos + 1};
	}

	constexpr static int Quit(char* args) { return -1; }

private:
	const char* symbol_;
	char* input_{nullptr};
	std::unordered_map<std::string, CommandFunction> cmd_map_;
	static inline const std::vector<std::string> kQuitCmds{"quit", "exit", "q"};
};

} // namespace netlib